`include "defines.v"

module ex(
input  wire                       reset,
	input  wire[`ALUOpBus]           aluop_input,
	input  wire[`ALUSelBus]          alusel_input,
	input  wire[`RegisterBus]        reg1_input,
	input  wire[`RegisterBus]        reg2_input,
	input  wire[`RegisterAddressBus] wd_input,
	input  wire                      wreg_input,
	input  wire[`RegisterBus]        hi_input,
	input  wire[`RegisterBus] 		  lo_input,
	input  wire[`RegisterBus]        mem_hi_input,
	input  wire[`RegisterBus] 		  mem_lo_input,
	input  wire					  mem_hilo_we,
	input  wire					  wb_hilo_we,
	input  wire[`RegisterBus] 		  wb_hi_input,
	input  wire[`RegisterBus] 		  wb_lo_input,
	output reg[`RegisterBus] 		  hi_o,
	output reg[`RegisterBus] 		  lo_o,
	output reg					  hilo_we,
	output reg[`RegisterAddressBus]  wd_output,
	output reg                       wreg_output,
	output reg[`RegisterBus]         wdata_output,
	output reg 					     stall_req,
	output reg[`RegisterBus]         div_op1,
	output reg[`RegisterBus]         div_op2,
	output reg						 div_start,
	output reg						 div_sign,
	input  wire[`DoubleRegisterBus]   div_res,
	input  wire						 div_done,
	
	input  wire[`RegisterBus]		 link_addr_i,
	input  wire						 is_in_ds_i
);

reg[`RegisterBus] logic_out;
reg[`RegisterBus] shift_out;
reg[`RegisterBus] arithmetic_out;
reg[`RegisterBus] hi_res;
reg[`RegisterBus] lo_res;
wire[`RegisterBus] add_mux;//原码补码选择器
wire			  ovflow_flag;//溢出标志
wire[`RegisterBus] sum_res;
wire				  reg1_less_reg2;
wire[`DoubleRegisterBus] mul_res_edit;
reg[`DoubleRegisterBus] mul_res; 
wire [`RegisterBus] mul_opt1;
wire [`RegisterBus] mul_opt2;
reg  temp;
reg stall_req_div;

assign add_mux = ((aluop_input == `SUB_OP)|| (aluop_input == `SUBU_OP) || (aluop_input == `SLT_OP)) ?(~reg2_input)+1 : reg2_input;//求补码
assign sum_res =  add_mux + reg1_input; //加法或减法运算
assign ovflow_flag = (!reg1_input[31]&&!add_mux[31]&&sum_res[31])||(reg1_input[31]&&add_mux[31]&&!sum_res[31]);
assign reg1_less_reg2 = (aluop_input == `SLT_OP) ? ((reg1_input[31]&&!reg2_input[31])||(!reg1_input[31]&&!reg2_input[31]&& sum_res[31])||(reg1_input[31]&&reg2_input[31]&&sum_res[31])):(reg1_input < reg2_input);
assign mul_opt1 = ((aluop_input == `MULT_OP) && (reg1_input[31] == 1'b1)) ? ((~ reg1_input) +1) : reg1_input;
assign mul_opt2 = ((aluop_input == `MULT_OP) && (reg2_input[31] == 1'b1)) ? (~reg2_input + 1) : reg2_input;
assign mul_res_edit =  mul_opt1 * mul_opt2;

	always @ (*) begin
		if(reset == `ResetEnable) begin
			mul_res <= {`ZeroWord,`ZeroWord};
		end else if((aluop_input == `MULT_OP) && (reg1_input[31] ^ reg2_input[31])) begin
			mul_res <= ~mul_res_edit + 1;
		end else begin
			mul_res <= mul_res_edit;
		end
	end

	always @(*) begin
		stall_req = stall_req_div;
	end

	always @ (*) begin
		if(reset == `ResetEnable) begin
			arithmetic_out <= `ZeroWord;
		end else begin 
			case(aluop_input)
				`SUB_OP,`SUBU_OP,`ADD_OP,`ADDU_OP:begin
					arithmetic_out <= sum_res;
				end 
				`SLT_OP,`SLTU_OP: begin
					arithmetic_out <= reg1_less_reg2;
				end
				`MULT_OP,`MULTU_OP:begin
					arithmetic_out <= `ZeroWord;
					hi_o <= mul_res[63:32];
					lo_o <= mul_res[31:0];
					hilo_we <= `WriteEnable;
				end
				`DIV_OP,`DIVU_OP:begin
					arithmetic_out <= `ZeroWord;
					hi_o <= div_res[63:32];
					lo_o <= div_res[31:0];
					hilo_we <= `WriteEnable;		
				end
				default:begin
					arithmetic_out <= `ZeroWord;
				end
			endcase
		end
	end

	always @ (*) begin
		if (reset == `ResetEnable) begin
			logic_out <= `ZeroWord;
		end else begin
			case (aluop_input)
				`OR_OP: begin
						logic_out <= reg1_input | reg2_input;
					end
				`AND_OP:begin
					logic_out <= reg1_input & reg2_input;
				end
				`NOR_OP:begin
					logic_out <= ~(reg1_input | reg2_input);
				end
				`XOR_OP:begin
					logic_out <= reg1_input ^ reg2_input;
				end
				`LUI_OP:begin
					logic_out <= reg1_input;
				end
				`MOV_OP:begin
						logic_out <= reg1_input;
				end
				`MFHI_OP:begin
					logic_out <= hi_res;
					hilo_we <= `WriteDisable;
				end
				
				`MFLO_OP:begin
					logic_out <= lo_res;
					hilo_we <= `WriteDisable;
				end

				`MTLO_OP:begin
					logic_out <= `ZeroWord;
					hi_o <= hi_res;
					lo_o <= reg1_input;
					hilo_we <= `WriteEnable;
				end

				`MTHI_OP:begin
					logic_out <= `ZeroWord;
					hi_o <= reg1_input;
					lo_o <= lo_res;
					hilo_we <= `WriteEnable;
				end
				default: begin
					logic_out <= `ZeroWord;
				end
			endcase
		end
	end

	always @ (*) begin
		if (reset == `ResetEnable) begin
			shift_out <= `ZeroWord;
		end else begin
			case (aluop_input)
				`SLL_OP: begin
						shift_out <= reg2_input << reg1_input[4:0];
					end
					`SRL_OP:begin
						shift_out <= reg2_input >> reg1_input[4:0];
					end
					`SRA_OP:begin
						shift_out <= $signed(reg2_input) >>> reg1_input[4:0];
					end
					default: begin
						shift_out <= `ZeroWord;
					end
			endcase
		end
	end

	always @ (*) begin
		if(reset == `ResetEnable) begin
			stall_req_div <= 1'b0;
			div_op1 <= `ZeroWord;
			div_op2 <= `ZeroWord;
			div_start <= 1'b0;
			div_start <= 1'b0;
			div_sign <= 1'b0;
		end else begin
			stall_req_div <= 1'b0;
			div_op1 <= `ZeroWord;
			div_op2 <= `ZeroWord;
			div_start <= 1'b0;
			div_start <= 1'b0;
			div_sign <= 1'b0;
			case(aluop_input)
				`DIV_OP:begin
					if(div_done == 1'b0) begin
						div_op1 <= reg1_input;
						div_op2 <= reg2_input;
						div_start <= 1'b1;
						div_sign <= 1'b1;
						stall_req_div <= 1'b1;
					end else if(div_done == 1'b1)begin
						div_op1 <= reg1_input;
						div_op2 <= reg2_input;
						div_start <= 1'b0;
						div_sign <= 1'b1;
						stall_req_div <= 1'b0;
					end else begin
						div_op1 <= `ZeroWord;
						div_op2 <= `ZeroWord;
						div_start <= 1'b0;
						div_start <= 1'b0;
						div_sign <= 1'b0;
						stall_req_div <= 1'b0;
					end
				end//this part might be finished.
				`DIVU_OP:begin
					if(div_done == 1'b0) begin
						div_op1 <= reg1_input;
						div_op2 <= reg2_input;
						div_start <= 1'b1;
						div_sign <= 1'b0;
						stall_req_div <= 1'b1;
					end else if(div_done == 1'b1)begin
						div_op1 <= reg1_input;
						div_op2 <= reg2_input;
						div_start <= 1'b0;
						div_sign <= 1'b0;
						stall_req_div <= 1'b0;
					end else begin
						div_op1 <= `ZeroWord;
						div_op2 <= `ZeroWord;
						div_start <= 1'b0;
						div_start <= 1'b0;
						div_sign <= 1'b0;
						stall_req_div <= 1'b0;
					end
				end
			endcase
		end 
	end

	always @ (*) begin
		wd_output <= wd_input;
		temp <= (aluop_input == `ADD_OP)||(aluop_input == `SUB_OP) && (ovflow_flag == 1'b1);
		if(((aluop_input == `ADD_OP)||(aluop_input == `SUB_OP)) && (ovflow_flag == 1'b1)) begin
			wreg_output <= 1'b0;
		end else begin
			wreg_output <= wreg_input;
		end

		case (alusel_input)
			`EXE_RES_LOGIC: begin
				wdata_output <= logic_out;
			end
			`EXE_RES_SHIFT: begin
				wdata_output <= shift_out;
			end
			`EXE_RES_ARITH: begin
				wdata_output <= arithmetic_out;
			end
			`EXE_RES_BRANCH:
				wdata_output <= link_addr_i;
			default: begin
				wdata_output <= `ZeroWord;
			end
		endcase
	end

	//read_hilo(data_forwarding)
	always @ (*) begin
		if(reset == `ResetEnable) begin
			hi_res <= `ZeroWord;
			lo_res <= `ZeroWord;
			hi_o <= `ZeroWord;
			lo_o <= `ZeroWord;
			hilo_we <= 1'b0;
		end else if(mem_hilo_we == `WriteEnable) begin
			hi_res <= mem_hi_input;
			lo_res <= mem_lo_input;
		end else if(wb_hilo_we == `WriteEnable) begin
			hi_res <= wb_hi_input;
			lo_res <= wb_lo_input;
		end else begin
			hi_res <= hi_input;
			lo_res <= lo_input;
		end 
	end

endmodule

